#include "drv_rtc.h"

#define RTC_ASYNCH_PREDIV          ((unsigned int)0x7FFF)

struct _drv_rtc_t {
    unsigned char month;
    unsigned char day;
    unsigned char year;
    unsigned char wday;
};

static struct _drv_rtc_t RTC_DateStruct = {
    .year   = 25,
    .month  = 1,
    .day    = 1,
    .wday   = 3
};

static unsigned int RTC_timestamp = 1735660830;
static unsigned char EndOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

void drv_rtc_init(void)
{
    LL_RTC_InitTypeDef rtc_initstruct = {0};
    /*##-1- Enable PWR clock and enable access to the backup domain #######*/
    /* To change the source clock of the RTC functionalities (LSE, LSI), you have to:
        - Enable the PWR clock
        - Enable write access to configure the RTC clock source (once after reset).
        - Reset the Backup domain
        - Configure the needed RTC clock source */
    // LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

    // LL_PWR_EnableBkUpAccess();
    // LL_RCC_LSE_Enable();

    while (LL_RCC_LSE_IsReady() != 1)
    {
    }

    if (LL_RCC_GetRTCClockSource() != LL_RCC_RTC_CLKSOURCE_LSE)
    {
        LL_RCC_ForceBackupDomainReset();
        LL_RCC_ReleaseBackupDomainReset();
        LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
    }

    /* Enable RTC clock and RTC APB clock */
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTC);
    LL_RCC_EnableRTC();
    
    /*##-4 Configure RTC ######################################### ############*/
    /* Configure RTC prescaler and RTC data registers */
    LL_RTC_DeInit(RTC);

    /* Configure prescaler and output source */
    rtc_initstruct.AsynchPrescaler = RTC_ASYNCH_PREDIV;
    rtc_initstruct.OutPutSource    = LL_RTC_CALIB_OUTPUT_NONE;
    LL_RTC_Init(RTC, &rtc_initstruct);


    LL_RTC_TimeTypeDef  rtc_time_initstruct = {0};
    rtc_time_initstruct.Hours      = 0;
    rtc_time_initstruct.Minutes    = 0;
    rtc_time_initstruct.Seconds    = 30;
    LL_RTC_TIME_Init(RTC, LL_RTC_FORMAT_BIN, &rtc_time_initstruct);
}

void drv_rtc_update(void)
{
    unsigned int RTC_Counter = LL_RTC_TIME_Get(RTC);

	//printf("hour = %d mins = %d secs = %d\n", (RTC_Counter / 3600), (RTC_Counter % 3600) / 60, (RTC_Counter % 3600) % 60);
		
	if (RTC_Counter >= 0x0001517FU) {	//1天
        if (RTC_DateStruct.day == EndOfMonth[RTC_DateStruct.month - 1]) {	//当前天数已经是最后一天
            RTC_DateStruct.day = 1U;	//天变成1
            if (RTC_DateStruct.month >= 0x0C) {	//月份大于12
                RTC_DateStruct.month = 0x01U;	//月份变成1
            } else {
                RTC_DateStruct.month += 1U;	//月份+1
            }
        } else {	
            RTC_DateStruct.day = RTC_DateStruct.day + 0x1U;	//天数+1
        }

        if (RTC_DateStruct.wday == 0x07U) {	//已经周日
            RTC_DateStruct.wday = 0x01U;	//周1
        } else {
            RTC_DateStruct.wday += 1;	//周+1
        }
        LL_RTC_TIME_SetCounter(RTC, 0x00);
    }
}

void drv_rtc_SetTime(rtc_date_time_t *dat)
{
    LL_RTC_TimeTypeDef  rtc_time_initstruct = {0};
    rtc_time_initstruct.Hours      = dat->time.hour;
    rtc_time_initstruct.Minutes    = dat->time.mins;
    rtc_time_initstruct.Seconds    = dat->time.secs;
    LL_RTC_TIME_Init(RTC, LL_RTC_FORMAT_BIN, &rtc_time_initstruct);

    RTC_DateStruct.year     = dat->date.year;
    RTC_DateStruct.month    = dat->date.month;
    RTC_DateStruct.day      = dat->date.day;
    RTC_DateStruct.wday     = dat->date.wday;
}

void drv_rtc_SetTimestamp(unsigned int tsmp)
{
    unsigned int RTC_Counter = LL_RTC_TIME_Get(RTC);
    if (tsmp <= RTC_Counter) {
        return ;
    } else {
        RTC_timestamp = tsmp - RTC_Counter;
    }
}

void drv_rtc_GetTime(rtc_date_time_t *dat)
{
    unsigned int RTC_Counter = LL_RTC_TIME_Get(RTC);
    dat->time.hour  = (RTC_Counter / 3600);
    dat->time.mins  = (RTC_Counter % 3600) / 60;
    dat->time.secs  = (RTC_Counter % 3600) % 60;

    dat->date.year  = RTC_DateStruct.year;
    dat->date.month = RTC_DateStruct.month;
    dat->date.day   = RTC_DateStruct.day;
    dat->date.wday  = RTC_DateStruct.wday;
}

unsigned int drv_rtc_GetTimestamp(void)
{
    return (RTC_timestamp + LL_RTC_TIME_Get(RTC));
}

